#include "NetworkPat.h"
#include "Debugging.h"
#include <fstream>

bool NetworkPat::ErrorCheck(const char* message)
{
  int err = WSAGetLastError();
  if(err == WSAEWOULDBLOCK)
  {
    return true;
  }
  GlobalDebug::GetInstance()->DbgOut("Error:");
  GlobalDebug::GetInstance()->DbgOut(message);
  return false;
}

NetworkPat::NetworkPat(void)
{
  mSuccess = Init();
}

bool NetworkPat::IsValid(void)
{
  return mSuccess;
}

bool NetworkPat::Init(void)
{
  int err = 0;
  mLocalScore = 0;
  mWin = false;
  SecureZeroMemory((void*)mRecvAddr, MAX_ADDRESS_LENGTH);
  
  err = WSAStartup(MAKEWORD(2,2), &mData);
  if(err)
  {
    GlobalDebug::GetInstance()->DbgOut("Error starting WinSock");
    return false;
  }
  err = mSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
  if(err == INVALID_SOCKET)
  {
    GlobalDebug::GetInstance()->DbgOut("Error creating local socket");
    return false;
  }

  //BIG_TODO : Config file reading
  hostent* localHost;
  localHost = gethostbyname("");
  char* addr = inet_ntoa(*(in_addr*)*localHost->h_addr_list);
  memcpy(mRecvAddr, addr, strlen(addr));

  std::ifstream input;
  input.open("ListenConfig.txt");
  if(input.fail())
    return false;
  input >> mPort;
  //input.ignore(100, '\n');
  //input.getline(mRecvAddr, MAX_ADDRESS_LENGTH);
  mLocal.sin_family = AF_INET;
  mLocal.sin_port = htons(mPort);
  mLocal.sin_addr.s_addr = inet_addr(addr);
  input.close();

  err = bind(mSocket, (SOCKADDR*)&mLocal, sizeof(mLocal));
  if(err == SOCKET_ERROR)
  {
    //react to error
    if(!ErrorCheck("Binding socket to local address"))
    {
      return false;
    }
  }

  unsigned long nonBlock = 1;
  err = ioctlsocket(mSocket, FIONBIO, &nonBlock);
  if(err)
  {
    //react to error
    if(!ErrorCheck("Setting socket to non-blocking"))
    {
      return false;
    }
  }
  Message temp(MessageType::SYSTEM, 0);
  mSizeArray[0] = sizeof(HEADER_BYTES);
  mSizeArray[1] = sizeof(temp.GetMessageType());
  mSizeArray[2] = sizeof(temp.GetMessageContent());
  mSizeArray[3] = sizeof(temp.GetMessageId());

  mTotalSize = 0;
  for(int i = 0; i < NUM_PARTS; ++i)
    mTotalSize += mSizeArray[i];

  mLocalId = 0;
  mPlayerCount = 0;
  mRandSeed = 0;
  return true;
}

NetworkPat::~NetworkPat(void)
{
  Cleanup();
  shutdown(mSocket, SD_BOTH);
  closesocket(mSocket);
  WSACleanup();
}
void NetworkPat::Reset(void)
{
  Cleanup();
  mSuccess = Init();
}

void NetworkPat::Cleanup(void)
{
  mLocalId = 0;
  mPlayerCount = 0;
  mRandSeed = 0;

  mClients.clear();

  std::list<Message*>::iterator iter;
  for(iter = msgList.begin(); iter != msgList.end(); ++iter)
  {
    if((*iter))
      delete (*iter);
  }
  msgList.clear();
  shutdown(mSocket, SD_BOTH);
  closesocket(mSocket);
  WSACleanup();
  return;
}

void NetworkPat::Send(Message* msg)
{
  char* buffer;
  int length;
  buffer = Serialize(msg, &length);

  int err = 0;
  for(unsigned int i = 0; i < mClients.size(); ++i)
  {
    err = sendto(mSocket, buffer, length, 0, (SOCKADDR*)&mClients[i], sizeof(mClients[i]));
    if(err == SOCKET_ERROR)
    {
      if(!ErrorCheck("Sending data to the remote address"))
      {
        //mSuccess = false;
      }
      else
      {
        //socket would have blocked, do something about it
      }
    }
  }
  delete msg;
  msg = NULL;
  delete [] buffer;
  return;
}

void NetworkPat::SendToOne(Message* msg, int id)
{
  char* buffer;
  int length;
  buffer = Serialize(msg, &length);
  int err = 0;
  err = sendto(mSocket, buffer, length, 0, (SOCKADDR*)&mClients[id], sizeof(mClients[id]));
  if(err == SOCKET_ERROR)
  {
    if(!ErrorCheck("Sending data to the remote address"))
    {
    }
    else
    {
    }
  }
  delete msg;
  delete [] buffer;
  return;
}

std::list<Message*>* NetworkPat::Recv(void)
{
  Message* msg = NULL;
  int err = 0;
  char buffer[1024] = {0};
  sockaddr_in fromAddr;
  int addrLen = sizeof(fromAddr);
  int count = 0;
  while(true)
  {
    err = recvfrom(mSocket, buffer, 1024, 0, (SOCKADDR*)&fromAddr, &addrLen);
    if(err == SOCKET_ERROR)
    {
      if(!ErrorCheck("Receiving data from remote address"))
      {
        //mSuccess = false;
      }
      else
      {
        //nothing to receive
        break;
      }
    }


    msg = Deserialize(buffer, err);
    if(msg != NULL)
    {
      if(mClients.size() < MAX_PLAYERS)
      {
        AddAddress(fromAddr);
        msgList.push_back(msg);
        ++count;
      }
    }

    //if(count == 20)
    //{
    //  //we've received 20 messages, that's a lot, we should stop
    //  break;
    //}
  }

  return &msgList;
}

void NetworkPat::AddAddress(sockaddr_in addr)
{
  for(unsigned int i = 0; i < mClients.size(); ++i)
  {
    if(mClients[i].sin_addr.s_addr == addr.sin_addr.s_addr)
    {
      return;
    }
  }
  mClients.push_back(addr);
  return;
}

void NetworkPat::RemoveAddress(int id)
{
  int sizeTest = (int)(mClients.size());
  if((id - 1) >= sizeTest)
    return;
  else if((id - 1) < 0)
    return;
  else
    mClients.erase(mClients.begin() + id);
  return;
}

char* NetworkPat::Serialize(Message *msg, int *length)
{
  int i;
  
  int id = msg->GetMessageId();
  int message = msg->GetMessageContent();
  MessageType type = msg->GetMessageType();

  void* partArray[NUM_PARTS] = {(void*)&HEADER_BYTES, (void*)&type, (void*)&message, (void*)&id};

  char* buffer = new char[mTotalSize];
  int location = 0;

  for(i = 0; i < NUM_PARTS; ++i)
  {
    memcpy(buffer + location, partArray[i], mSizeArray[i]);
    location += mSizeArray[i];
  }
  *length = mTotalSize;
  return buffer;
}

Message* NetworkPat::Deserialize(char* buffer, int length)
{
  if(length != mTotalSize)
  {
    //buffer is the wrong size to be one of our network messages
    return NULL;
  }
  unsigned int check;
  MessageType type;
  int content;
  int id;

  int location = 0;
  void* partArray[NUM_PARTS] = {&check, &type, &content, &id};
  for(int i = 0; i < NUM_PARTS; ++i)
  {
    memcpy(partArray[i], buffer + location, mSizeArray[i]);
    location += mSizeArray[i];
  }
  if(check != HEADER_BYTES)
  {
    //not one of my packets
    return NULL;
  }
  Message* msg = new Message(type, content);
  msg->SetMessageId(id);
  return msg;
}

void NetworkPat::SetId(int id)
{
  mLocalId = id;
}

int NetworkPat::GetId(void)
{
  return mLocalId;
}

void NetworkPat::SetCount(int count)
{
  mPlayerCount = count;
}

int NetworkPat::GetCount(void)
{
  return mPlayerCount;
}

void NetworkPat::SetSeed(int seed)
{
  mRandSeed = seed;
}

int NetworkPat::GetSeed(void)
{
  return mRandSeed;
}

char* NetworkPat::GetAddr(void)
{
  return mRecvAddr;
}

unsigned short NetworkPat::GetPort(void)
{
  return mPort;
}

int NetworkPat::IncrementLocalScore(int inc)
{
  mLocalScore += inc;
  return mLocalScore;
}

void NetworkPat::SetWin(bool win)
{
  mWin = win;
  return;
}
bool NetworkPat::GetWin(void)
{
  return mWin;
}

unsigned int NetworkPat::GetAddrCount(void)
{
  return mClients.size();
}
